{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Inside Quantum Classifiers\n",
    "\n",
    "This tutorial offers a deep dive in the internals of quantum classifiers and several exercises on solving a simple classification problem from scratch. You will learn how to build a simple classifier by hand and how to train a simple model using the [quantum machine learning library](https://docs.microsoft.com/azure/quantum/user-guide/libraries/machine-learning/intro) that is part of the Microsoft Quantum Development Kit. \n",
    "\n",
    "The companion Python notebook [Exploring Quantum Classification Library](./ExploringQuantumClassificationLibrary.ipynb) offers a high-level walk-through of solving the same classification problem.\n",
    "\n",
    "### Prerequisites\n",
    "\n",
    "We recommend you to get familiar with the basics of quantum computing and quantum programming before attempting this tutorial. It relies on the concepts of superposition and measurement, as well as the basic knowledge of quantum gates and circuits.\n",
    "\n",
    "* You can use the [other tutorials](../../index.ipynb) to learn the basic concepts of quantum computing and their representation in Q#.\n",
    "* Alternatively, [Microsoft Quantum Development Kit documentation](https://docs.microsoft.com/azure/quantum) covers a lot of introductory topics.\n",
    "\n",
    "The necessary functionality is provided in the [Microsoft.Quantum.MachineLearning package](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning) package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Circuit-centric Quantum Classifiers: an Overview\n",
    "\n",
    "Circuit-centric quantum classifiers have the following structure:\n",
    "\n",
    "1. Data encoding.  \n",
    "The features of a data sample are encoded as the amplitudes of a quantum state. This state further serves as the input to the classification circuit. The encoding is parameter-free and uses only the raw data of the sample.\n",
    "\n",
    "2. Classification model circuit.  \n",
    "The classification circuit is a small-depth circuit of single-qubit rotations and two-qubit controlled rotations. \n",
    "The circuit geometry (the sequence of the rotations and the qubits to which they are applied) is fixed for the model, and the parameters of the model (the rotation angles) are learned during the training phase.\n",
    "\n",
    "3. Measurement.  \n",
    "Applying the classification circuit to the data encoded as a quantum state yields the final quantum state. \n",
    "Measuring the \"output\" qubit of that state allows us to get a classical result - 0 or 1. \n",
    "However, that is not the final classification result yet...\n",
    "\n",
    "4. Result interpretation.  \n",
    "The steps 1-3 are repeated multiple times to estimate the probability of getting 0 or 1 in the final measurement.\n",
    "This probability is compared with a threshold (classifier bias) to produce the final classification result.\n",
    "\n",
    "You can read more about the theory behind circuit-centric quantum classifiers in the [Microsoft QDK documentation](https://docs.microsoft.com/azure/quantum/user-guide/libraries/machine-learning/intro) or in the [original paper](https://arxiv.org/abs/1804.00633). \n",
    "In this tutorial we will focus on walking through each step of the classification and training process and implementing them."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. The Raw Data\n",
    "\n",
    "Same as in the companion tutorial, we start by preparing the training and validation datasets. \n",
    "\n",
    "> Q# is a domain-specific programming language, designed to express quantum algorithms, but it supports a subset of classical language features sufficient for performing simple computations, such as data generation. When solving a real problem, you'll want to load the data from an external source before calling Q# code to process it.\n",
    "\n",
    "This Q# code follows the same data generation logic as the [Python code](./ExploringQuantumClassificationLibrary.ipynb#The-Data) in the companion notebook. \n",
    "There are two features, each of them is a real number from $[0, 1)$ range."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": "[\"SampleData\",\"SampleDataDemo\"]",
      "text/html": [
       "<ul><li>SampleData</li><li>SampleDataDemo</li></ul>"
      ],
      "text/plain": [
       "SampleData, SampleDataDemo"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "open Microsoft.Quantum.Math;\n",
    "open Microsoft.Quantum.Random;\n",
    "\n",
    "operation SampleData (samplesNumber : Int, separationAngles : Double[]) : (Double[][], Int[]) {\n",
    "    mutable features = [];\n",
    "    mutable labels = [];\n",
    "    for i in 0 .. samplesNumber - 1 {\n",
    "        let sample = [DrawRandomDouble(0.0, 1.0), DrawRandomDouble(0.0, 1.0)];\n",
    "        let angle = ArcTan2(sample[1], sample[0]);\n",
    "        set features += [sample];\n",
    "        set labels += [(angle < separationAngles[0] or angle > separationAngles[1]) ? 0 | 1];\n",
    "    }\n",
    "    return (features, labels);\n",
    "}\n",
    "\n",
    "operation SampleDataDemo () : Unit {\n",
    "    let trainingData = SampleData(5, [PI() / 6.0, PI()/ 3.0]);\n",
    "    Message($\"{trainingData}\");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`%simulate` cells run the Q# code on a quantum simulator. Executing the following cell shows a small sample of data generated by the code above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "([[0,41558553018401634,0,8455760422374942],[0,4469224612447072,0,16762420449760937],[0,4400985573605162,0,05275775680912554],[0,1748506683739138,0,9069414613335121],[0,8807629122774875,0,7115352604126256]], [0,0,0,0,1])\r\n"
     ]
    },
    {
     "data": {
      "application/json": "{\"@type\":\"tuple\"}",
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%simulate SampleDataDemo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Data Encoding\n",
    "\n",
    "The first step of the quantum classification process is encoding the raw feature data into the amplitudes of a quantum state. \n",
    "\n",
    "> If you need a refresher on quantum state representation, see [Multi-qubit Systems tutorial](https://github.com/microsoft/QuantumKatas/tree/main/tutorials/MultiQubitSystems).\n",
    "\n",
    "An $n$-qubit quantum state can be described by $2^n$ amplitudes. \n",
    "If the data has $M$ features, it can be encoded in the amplitudes of a state with $n = \\lceil \\log_2 M \\rceil$ qubits. \n",
    "In general case the amplitudes can be complex, but for the purposes of data encoding it's sufficient to use only real amplitudes.\n",
    "\n",
    "In our case $M = 2$, so we can encode the features $(x_0, x_1)$ in the state of one qubit as the amplitudes of the basis states $|0\\rangle$ and $|1\\rangle$, respectively. \n",
    "The sum of squares of the amplitudes of the basis states has to be 1, so we'll have to normalize our data:\n",
    "\n",
    "$$(x_0, x_1) \\rightarrow |\\psi(x_0, x_1) \\rangle = \\tilde{x}_0 |0\\rangle + \\tilde{x}_1 |1\\rangle \\text{, where }\n",
    "\\tilde{x}_0 = \\frac{x_0}{\\sqrt{x_0^2 + x_1^2}}\\text{ and }\\tilde{x}_1 = \\frac{x_1}{\\sqrt{x_0^2 + x_1^2}}$$\n",
    "\n",
    "> Note that this encoding will lose part of the information: if we plot the data, multiple points $(x_0, x_1)$ will be encoded in the same $|\\psi(x_0, x_1) \\rangle$. \n",
    "> Effectively only the angular data $\\alpha$ is preserved, and the data about the distance to the origin is lost. \n",
    ">\n",
    "> If we plot our data on a plane with X and Y axis corresponding to the amplitudes of the $|0\\rangle$ and $|1\\rangle$ states, our normalized data will belong to the unit circle:\n",
    "> <img src=\"./img/1-data-encoding.PNG\" width=300 alt=\"Two distinct data points are encoded as the same state\" />\n",
    ">\n",
    "> If we need to preserve information about the distance to the origin (which is typically the case), we need to pre-process the data, adding an extra feature.\n",
    "> In this tutorial we'll omit this step for simplicity; the synthetic data we're using is chosen so that only the angular data defines the class of the sample.\n",
    "\n",
    "To implement data encoding in Q#, we can use library routines \n",
    "[InputEncoder](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.inputencoder) or \n",
    "[ApproximateInputEncoder](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.approximateinputencoder).\n",
    "Here is what the results will look like for one sample:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": "[\"EncodeDataDemo\"]",
      "text/html": [
       "<ul><li>EncodeDataDemo</li></ul>"
      ],
      "text/plain": [
       "EncodeDataDemo"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "open Microsoft.Quantum.Arithmetic;\n",
    "open Microsoft.Quantum.Diagnostics;\n",
    "open Microsoft.Quantum.Math;\n",
    "open Microsoft.Quantum.MachineLearning;\n",
    "open Microsoft.Quantum.Random;\n",
    "\n",
    "operation EncodeDataDemo () : Unit {\n",
    "    let sample = [DrawRandomDouble(0.0, 1.0), DrawRandomDouble(0.0, 1.0)];\n",
    "    Message($\"Raw data: {sample}\");\n",
    "    \n",
    "    let norm = Sqrt(sample[0] ^ 2.0 + sample[1] ^ 2.0);\n",
    "    Message($\"Normalized data: [{sample[0] / norm}, {sample[1] / norm}]\");\n",
    "    \n",
    "    use q = Qubit();\n",
    "    let (_, encoder) = (InputEncoder(sample))!;\n",
    "    encoder(LittleEndian([q]));\n",
    "    Message(\"Encoded as a quantum state:\");\n",
    "    DumpMachine();\n",
    "    Reset(q);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Raw data: [0,2991085887416772,0,2821816500659015]\n",
      "Normalized data: [0,7273891172943013, 0,6862252341919649]\n",
      "Encoded as a quantum state:\n"
     ]
    },
    {
     "data": {
      "application/json": "{\"div_id\":\"dump-machine-div-9aceb95a-a721-421d-a5f3-8da9588bf3dd\",\"qubit_ids\":[0],\"n_qubits\":1,\"amplitudes\":[{\"Real\":0.7273891172943014,\"Imaginary\":0.0,\"Magnitude\":0.7273891172943014,\"Phase\":0.0},{\"Real\":0.686225234191965,\"Imaginary\":0.0,\"Magnitude\":0.686225234191965,\"Phase\":0.0}]}",
      "text/html": [
       "\r\n",
       "                    <table style=\"table-layout: fixed; width: 100%\">\r\n",
       "                        <thead>\r\n",
       "                            \r\n",
       "                        <tr>\r\n",
       "                            <th>Qubit IDs</th>\r\n",
       "                            <td span=\"3\">0</td>\r\n",
       "                        </tr>\r\n",
       "                    \r\n",
       "                            <tr>\r\n",
       "                                <th style=\"width: 20ch)\">Basis state (little endian)</th>\r\n",
       "                                <th style=\"width: 20ch\">Amplitude</th><th style=\"width: calc(100% - 26ch - 20ch)\">Meas. Pr.</th><th style=\"width: 6ch\">Phase</th>\r\n",
       "                            </tr>\r\n",
       "                        </thead>\r\n",
       "                        <tbody>\r\n",
       "                        \r\n",
       "                            <tr>\r\n",
       "                                <td>$\\left|0\\right\\rangle$</td>\r\n",
       "                                <td>$0.7274 + 0.0000 i$</td>\r\n",
       "                                \r\n",
       "                                <td>\r\n",
       "                                    <progress\r\n",
       "                                        max=\"100\"\r\n",
       "                                        value=\"52.9094927958183\"\r\n",
       "                                        style=\"width: 100%;\"\r\n",
       "                                    > \r\n",
       "                                    <td>\r\n",
       "                                    <p id=\"round-0f3d530d-b4cc-4920-afe3-638111695e5a\"> \r\n",
       "                                    <script>\r\n",
       "                                    var num = 52.9094927958183;\r\n",
       "                                    num = num.toFixed(4);\r\n",
       "                                    var num_string = num + \"%\";\r\n",
       "                                     document.getElementById(\"round-0f3d530d-b4cc-4920-afe3-638111695e5a\").innerHTML = num_string;\r\n",
       "                                    </script> </p>\r\n",
       "                                    </td>\r\n",
       "                                </td>\r\n",
       "                            \r\n",
       "                                \r\n",
       "                                <td style=\"transform: rotate(0deg);\r\n",
       "                   text-align: center;\">\r\n",
       "                                 ↑\r\n",
       "                                </td>\r\n",
       "                            \r\n",
       "                            </tr>\r\n",
       "                        \n",
       "\r\n",
       "                            <tr>\r\n",
       "                                <td>$\\left|1\\right\\rangle$</td>\r\n",
       "                                <td>$0.6862 + 0.0000 i$</td>\r\n",
       "                                \r\n",
       "                                <td>\r\n",
       "                                    <progress\r\n",
       "                                        max=\"100\"\r\n",
       "                                        value=\"47.090507204181726\"\r\n",
       "                                        style=\"width: 100%;\"\r\n",
       "                                    > \r\n",
       "                                    <td>\r\n",
       "                                    <p id=\"round-8ee190ed-a26f-4390-86fe-07ef2e4c2d5c\"> \r\n",
       "                                    <script>\r\n",
       "                                    var num = 47.090507204181726;\r\n",
       "                                    num = num.toFixed(4);\r\n",
       "                                    var num_string = num + \"%\";\r\n",
       "                                     document.getElementById(\"round-8ee190ed-a26f-4390-86fe-07ef2e4c2d5c\").innerHTML = num_string;\r\n",
       "                                    </script> </p>\r\n",
       "                                    </td>\r\n",
       "                                </td>\r\n",
       "                            \r\n",
       "                                \r\n",
       "                                <td style=\"transform: rotate(0deg);\r\n",
       "                   text-align: center;\">\r\n",
       "                                 ↑\r\n",
       "                                </td>\r\n",
       "                            \r\n",
       "                            </tr>\r\n",
       "                        \r\n",
       "                        </tbody>\r\n",
       "                    </table>"
      ],
      "text/plain": [
       "|0⟩\t0,7273891172943014 + 0𝑖\n",
       "|1⟩\t0,686225234191965 + 0𝑖"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/json": "{\"@type\":\"tuple\"}",
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%simulate EncodeDataDemo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Classification Model Circuit\n",
    "\n",
    "The classification circuit is a small-depth circuit of single-qubit rotations and two-qubit controlled rotations.\n",
    "It can be described using two types of data:\n",
    "\n",
    "1. The circuit geometry is the sequence of the gates that comprise the circuit, the types of rotations each of them uses and the qubits to which each of them is applied.  \n",
    "Similarly to the model architecture in traditional machine learning, the circuit geometry is fixed for a model. \n",
    "In our case we have only one qubit to build the circuit on, so our choices are limited to only three single-qubit rotation gates: \n",
    "[$R_x$](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.rx), \n",
    "[$R_y$](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.ry) and\n",
    "[$R_z$](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.rz). \n",
    "(You can read more about these gates in the [single-qubit gates tutorial](https://github.com/microsoft/QuantumKatas/tree/main/tutorials/SingleQubitGates).)\n",
    "We will make an educated guess and decide to use a single [$R_y$](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.intrinsic.ry) gate - we will see why it works for our data later. Here is our model circuit:\n",
    "\n",
    "<img src=\"./img/2-classification-circuit.PNG\" width=250 alt=\"Circuit consisting of rotation gate\" />\n",
    "\n",
    "2. The parameters of each gate (the rotation angles).  \n",
    "Thee parameters are learned during the training phase.\n",
    "In our case the model has exactly one parameter: the rotation angle of our $R_y$ gate. \n",
    "\n",
    "> Following the same data visualization scheme as before, applying $R_y$ gate with parameter $\\theta$ $R_y(\\theta)$ will rotate the vector describing the state of the qubit counter-clockwise by $\\frac{\\theta}{2}$ radians.\n",
    "> <img src=\"./img/3-rotation-gate.PNG\" width=300 alt=\"Two distinct data points are encoded as the same state\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Measurement and Result Interpretation\n",
    "\n",
    "The last step of the classification run is measuring the output qubit. \n",
    "This step is probabilistic: unless the qubit ends up in one of the basis states, the measurement can yield both $0$ and $1$ in different runs. \n",
    "To get a useful interpretation of the measurement result, we will repeat all steps (data encoding, applying classifier circuit and measurement) multiple times to estimate the *probabilities* of measuring $0$ and $1$.\n",
    "\n",
    "Finally, we will compare this probability with a threshold $0.5-b$, where $b$ is the *classifier bias* - another parameter of the classification process that is learned during the training phase. \n",
    "We assign the label to the sample we're classifying depending on whether the probability is below or above this threshold."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Using the QML Library\n",
    "\n",
    "We could implement the whole training and classification process by hand, but this would become quite tedious very fast. \n",
    "Indeed, traditional machine learning uses well-developed libraries to abstract away the low-level implementation details and to allow the user to focus on high-level properties of the model.\n",
    "The `Microsoft.Quantum.MachineLearning` library shipped with the Quantum Development Kit does just that for the quantum classifier we discuss in this tutorial. Let's see how this model looks when using the library.\n",
    "\n",
    "### 6.1. Representing the circuit geometry\n",
    "\n",
    "To describe the model in the format required for using the library, we'll need to represent the circuit geometry as an array of [ControlledRotation](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.controlledrotation)s, one per gate. \n",
    "You can read more about designing classifier circuits in [the documentation](https://docs.microsoft.com/azure/quantum/user-guide/libraries/machine-learning/design).\n",
    "\n",
    "In our case we need to define an array of one rotation around PauliY axis that will be applied to qubit with index 0, with an empty array of control qubits, and parameterized with parameter with index 0. The following function does exactly that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": "[\"ClassifierStructure\"]",
      "text/html": [
       "<ul><li>ClassifierStructure</li></ul>"
      ],
      "text/plain": [
       "ClassifierStructure"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "open Microsoft.Quantum.MachineLearning;\n",
    "\n",
    "function ClassifierStructure () : ControlledRotation[] {\n",
    "    return [\n",
    "        ControlledRotation((0, []), PauliY, 0)\n",
    "    ];\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2. Training the model\n",
    "\n",
    "Library operation [TrainSequentialClassifier](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.trainsequentialclassifier) \n",
    "encapsulates all the model training logic."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": "[\"TrainModel\",\"TrainModelDemo\"]",
      "text/html": [
       "<ul><li>TrainModel</li><li>TrainModelDemo</li></ul>"
      ],
      "text/plain": [
       "TrainModel, TrainModelDemo"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "open Microsoft.Quantum.Arrays;\n",
    "open Microsoft.Quantum.MachineLearning;\n",
    "open Microsoft.Quantum.Math;\n",
    "\n",
    "operation TrainModel (\n",
    "    trainingVectors : Double[][],\n",
    "    trainingLabels : Int[],\n",
    "    initialParameters : Double[][]\n",
    ") : (Double[], Double) {\n",
    "    // Combine training data and labels into a single data structure\n",
    "    let samples = Mapped(\n",
    "        LabeledSample,\n",
    "        Zipped(trainingVectors, trainingLabels)\n",
    "    );\n",
    "    \n",
    "    // Define a set of models we're going to try training;\n",
    "    // in this case all models have the same structure but differ in the value of initial parameters\n",
    "    let models = Mapped(\n",
    "        SequentialModel(ClassifierStructure(), _, 0.0),\n",
    "        initialParameters\n",
    "    );\n",
    "    \n",
    "    // use all samples both for training and for validation\n",
    "    let defaultSchedule = SamplingSchedule([0..Length(samples) - 1]);\n",
    "    let (optimizedModel, nMisses) = TrainSequentialClassifier(\n",
    "        models,\n",
    "        samples,\n",
    "        DefaultTrainingOptions()\n",
    "            w/ LearningRate <- 2.0\n",
    "            w/ Tolerance <- 0.0005,\n",
    "        defaultSchedule,\n",
    "        defaultSchedule\n",
    "    );\n",
    "    Message($\"Training complete, found optimal parameters: {optimizedModel::Parameters}, {optimizedModel::Bias} with {nMisses} misses\");\n",
    "    return (optimizedModel::Parameters, optimizedModel::Bias);\n",
    "}\n",
    "\n",
    "operation TrainModelDemo () : Unit {\n",
    "    // generate the training data\n",
    "    let (features, labels) = SampleData(150, [PI() / 6.0, PI()/ 3.0]);\n",
    "    let (parameters, bias) = TrainModel(features, labels, [[1.0], [2.0]]);\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training complete, found optimal parameters: [1,5743999999999962], -0,43235 with 0 misses\r\n"
     ]
    },
    {
     "data": {
      "application/json": "{\"@type\":\"tuple\"}",
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%simulate TrainModelDemo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.3. Using the trained model for classification/validation\n",
    "\n",
    "Now that we have trained the model, we can use it either for classifying data using [EstimateClassificationProbabilities](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.estimateclassificationprobabilities) and \n",
    "[InferredLabels](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.inferredlabels) library operations, \n",
    "or for validating the model using [ValidateSequentialClassifier](https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.machinelearning.validatesequentialclassifier).\n",
    "\n",
    "In the [Python notebook](./ExploringQuantumClassificationLibrary.ipynb) we used the trained model to classify validation data and to plot it afterwards. \n",
    "In this case we don't want to build any plots, so let's just validate the model using another set of data, generated using the same procedure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": "[\"ValidateModel\",\"TrainAndValidateModelDemo\"]",
      "text/html": [
       "<ul><li>ValidateModel</li><li>TrainAndValidateModelDemo</li></ul>"
      ],
      "text/plain": [
       "ValidateModel, TrainAndValidateModelDemo"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "open Microsoft.Quantum.Arrays;\n",
    "open Microsoft.Quantum.Convert;\n",
    "open Microsoft.Quantum.MachineLearning;\n",
    "open Microsoft.Quantum.Math;\n",
    "\n",
    "operation ValidateModel (\n",
    "    validationVectors : Double[][],\n",
    "    validationLabels : Int[],\n",
    "    parameters : Double[],\n",
    "    bias : Double\n",
    ") : Double {\n",
    "    // Combine training data and labels into a single data structure\n",
    "    let samples = Mapped(\n",
    "        LabeledSample,\n",
    "        Zipped(validationVectors, validationLabels)\n",
    "    );\n",
    "\n",
    "    let tolerance = 0.005;\n",
    "    let nMeasurements = 10000;\n",
    "    // use all data for validation\n",
    "    let defaultSchedule = SamplingSchedule([0..Length(samples) - 1]);    \n",
    "    let results = ValidateSequentialClassifier(\n",
    "        SequentialModel(ClassifierStructure(), parameters, bias),\n",
    "        samples,\n",
    "        tolerance,\n",
    "        nMeasurements,\n",
    "        defaultSchedule\n",
    "    );\n",
    "    return IntAsDouble(results::NMisclassifications) / IntAsDouble(Length(samples));\n",
    "}\n",
    "\n",
    "operation TrainAndValidateModelDemo () : Unit {\n",
    "    // generate the training data\n",
    "    let (trainingVectors, trainingLabels) = SampleData(150, [PI() / 6.0, PI()/ 3.0]);\n",
    "    let (parameters, bias) = TrainModel(trainingVectors, trainingLabels, [[1.0], [2.0]]);\n",
    "    \n",
    "    // generate the validation data\n",
    "    let (validationVectors, validationLabels) = SampleData(50, [PI() / 6.0, PI()/ 3.0]);\n",
    "    let missRate = ValidateModel(validationVectors, validationLabels, parameters, bias);\n",
    "    Message($\"Miss rate: {missRate * 100.0}%\");\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training complete, found optimal parameters: [1,5632000000000161], -0,43415 with 0 misses\n",
      "Miss rate: 0%\n"
     ]
    },
    {
     "data": {
      "application/json": "{\"@type\":\"tuple\"}",
      "text/plain": [
       "()"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%simulate TrainAndValidateModelDemo"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Q#",
   "language": "qsharp",
   "name": "iqsharp"
  },
  "language_info": {
   "file_extension": ".qs",
   "mimetype": "text/x-qsharp",
   "name": "qsharp",
   "version": "0.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
